package com.sandyz.virtualcam.hooks

import android.content.res.XModuleResources
import android.graphics.SurfaceTexture
import android.hardware.camera2.CameraCaptureSession
import android.hardware.camera2.CameraDevice
import android.hardware.camera2.CaptureRequest
import android.hardware.camera2.params.InputConfiguration
import android.hardware.camera2.params.OutputConfiguration
import android.hardware.camera2.params.SessionConfiguration
import android.os.Build
import android.os.Handler
import android.view.Surface
import com.sandyz.virtualcam.utils.PlayIjk
import com.sandyz.virtualcam.utils.xLog
import de.robv.android.xposed.XC_MethodHook
import de.robv.android.xposed.XposedBridge
import de.robv.android.xposed.XposedHelpers
import de.robv.android.xposed.callbacks.XC_LoadPackage.LoadPackageParam
import tv.danmaku.ijk.media.player.IjkMediaPlayer

/**
 * 抖音和小米相机虚拟摄像头实现
 * 
 * 主要功能：
 * 1. 拦截系统相机API调用
 * 2. 替换真实摄像头数据
 * 3. 注入虚拟视频流
 * 4. 处理不同Android版本的兼容性
 * 
 * 实现细节：
 * - 使用Surface和SurfaceTexture处理视频数据
 * - 使用ijkplayer播放视频流
 * - 支持多种相机API（Camera2 API）
 * - 适配不同厂商的相机实现
 * 
 * @author sandyz987
 * @date 2023/11/18
 */
class VirtualCameraDy : IHook {
    // 1. 基本信息实现
    override fun getName(): String = "抖音和小米相机可用的虚拟摄像头模块"
    override fun getSupportedPackages() = listOf(
        "com.android.camera",      // 小米相机
        "com.ss.android.ugc.aweme", // 抖音
        "tv.danmaku.bili",         // 哔哩哔哩
        "com.tencent.mm",          // 微信
    )

    // 2. 初始化方法
    override fun init(cl: ClassLoader?) {
        // 初始化环境，目前为空实现
    }

    override fun registerRes(moduleRes: XModuleResources?) {
        // 注册资源，目前为空实现
    }

    // 3. 核心变量定义
    private var nullSurface: Surface? = null           // 空Surface，用于替换真实摄像头输出
    private var nullSurfaceTex: SurfaceTexture? = null // SurfaceTexture，用于创建空Surface
    private var ijkMediaPlayer: IjkMediaPlayer? = null // 视频播放器实例
    private var virtualSurface: Surface? = null        // 虚拟Surface，用于显示视频

    // 4. 回调类定义
    // 要用class来hook接口，因为接口不能直接hook
    // 这些callback类设置为全局变量的原因是，如果为null就执行，确保只执行一次（防止多次hook同一个方法）
    //    private var deviceStateCallbackClazz: Class<*>? = null
    private var sessionStateCallbackClazz: Class<*>? = null // 会话状态回调类

    /**
     * 5. 创建新的ijkplayer实例
     * 由于库已加载，直接创建实例即可
     */
    private fun newIjkMediaPlayer(): IjkMediaPlayer = IjkMediaPlayer {}

    /**
     * 6. 重置ijkplayer
     * 停止当前播放并释放资源，创建新实例
     */
    private fun resetIjkMediaPlayer() {
        if (ijkMediaPlayer?.isPlaying == true) {
            ijkMediaPlayer?.stop()
        }
        ijkMediaPlayer?.release()
        ijkMediaPlayer = newIjkMediaPlayer()
    }

    /**
     * 7. 重置Surface
     * 释放旧的Surface资源，创建新的空Surface
     */
    private fun resetSurface() {
        xLog("重置surface")
        virtualSurface = null
        nullSurfaceTex?.release()
        nullSurface?.release()
        nullSurfaceTex = SurfaceTexture(15)
        nullSurface = Surface(nullSurfaceTex)
    }

    /**
     * 8. Hook实现
     * 拦截并修改相机相关API调用
     */
    override fun hook(lpparam: LoadPackageParam?) {
        // 8.0 检查应用包名，只对支持的应用进行hook
        val packageName = lpparam?.packageName
        if (packageName !in getSupportedPackages()) {
            xLog("应用 $packageName 不在支持列表中，跳过hook")
            return
        }
        xLog("开始hook应用: $packageName")

        // 8.1 拦截ImageReader创建
        XposedHelpers.findAndHookMethod(
            "android.media.ImageReader", lpparam!!.classLoader, "newInstance",
            Int::class.javaPrimitiveType,
            Int::class.javaPrimitiveType,
            Int::class.javaPrimitiveType,
            Int::class.javaPrimitiveType, object : XC_MethodHook() {
                override fun beforeHookedMethod(param: MethodHookParam?) {
                    xLog("创建图片读取器ImageReader.newInstance   宽：${param?.args?.get(0)}，高：${param?.args?.get(1)}，格式：${param?.args?.get(2)}")
                }
            })

        // 8.2 拦截Camera2 API的createCaptureSession（Android 9及以上）
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
            XposedHelpers.findAndHookMethod("android.hardware.camera2.impl.CameraDeviceImpl", 
                lpparam.classLoader, "createCaptureSession", 
                SessionConfiguration::class.java, object : XC_MethodHook() {
                override fun beforeHookedMethod(param: MethodHookParam) {
                    resetSurface()

                    xLog("应用程序创建相机管道?createCaptureSession   ")
                    val sessionConfiguration = param.args[0] as SessionConfiguration

                    // 8.2.1 获取原始Surface列表
                    val surfaces = mutableListOf<Surface?>()
                    sessionConfiguration.outputConfigurations.forEach {
                        surfaces.add(it.surface)
                    }
                    xLog("应用程序想要添加surfaces: $surfaces，拦截后只添加：$nullSurface")

                    // 8.2.2 创建新的会话配置
                    val outputConfiguration = nullSurface?.let { OutputConfiguration(it) }
                    val mySessionConfiguration =
                        SessionConfiguration(
                            sessionConfiguration.sessionType,
                            listOf(outputConfiguration),
                            sessionConfiguration.executor,
                            sessionConfiguration.stateCallback
                        )
                    param.args[0] = mySessionConfiguration

                    // 8.2.3 Hook会话状态回调
                    hookSessionStateCallback(sessionConfiguration.stateCallback.javaClass)
                }
            })
        }

        // 8.3 适配OPPO的createCaptureSession实现
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            try {
                XposedHelpers.findAndHookMethod(
                    "com.color.compat.hardware.camera2.CameraDevicesNative", 
                    lpparam.classLoader, "createCustomCaptureSession",
                    CameraDevice::class.java,
                    InputConfiguration::class.java,
                    List::class.java,
                    Int::class.javaPrimitiveType,
                    CameraCaptureSession.StateCallback::class.java,
                    Handler::class.java, object : XC_MethodHook() {
                        override fun beforeHookedMethod(param: MethodHookParam?) {
                            xLog("oppo应用程序创建相机管道?createCaptureSession   ")
                            // 8.3.1 获取原始Surface列表
                            val surfaces = mutableListOf<Surface?>()
                            (param?.args?.get(2) as List<*>).forEach {
                                if (it is Surface) {
                                    surfaces.add(it)
                                }
                            }
                            xLog("oppo应用程序想要添加surfaces: $surfaces，拦截后只添加：$nullSurface")
                            // 8.3.2 替换为虚拟Surface
                            param.args[2] = listOf(nullSurface?.let { OutputConfiguration(it) })

                            // 8.3.3 Hook会话状态回调
                            hookSessionStateCallback(param.args[4].javaClass)
                        }
                    })
            } catch (e: Throwable) {
                xLog("oppo没有找到自定义方法createCustomCaptureSession")
            }
        }

        // 8.4 拦截addTarget方法
        XposedHelpers.findAndHookMethod("android.hardware.camera2.CaptureRequest.Builder", 
            lpparam.classLoader, "addTarget", Surface::class.java, object : XC_MethodHook() {
            override fun beforeHookedMethod(param: MethodHookParam?) {
                xLog("应用程序向相机添加输出目标addTarget          surface: ${param?.args?.get(0)}")
                xLog("找到屏幕上的surface          surface: ${param?.args?.get(0)}")
                // 8.4.1 记录显示Surface
                if (virtualSurface == null) {
                    virtualSurface = param?.args?.get(0) as Surface?
                    resetIjkMediaPlayer()
                    PlayIjk.play(virtualSurface, ijkMediaPlayer)
                }
                // 8.4.2 替换为虚拟Surface
                param?.args?.set(0, nullSurface)
            }
        })

        // 8.5 拦截startPreview方法
        XposedHelpers.findAndHookMethod("android.hardware.Camera", 
            lpparam.classLoader, "startPreview", object : XC_MethodHook() {
            override fun beforeHookedMethod(param: MethodHookParam?) {
                xLog("应用程序开始预览startPreview         屏幕surface: $virtualSurface")
            }
        })

        // 8.6 拦截build方法
        XposedHelpers.findAndHookMethod("android.hardware.camera2.CaptureRequest.Builder", 
            lpparam.classLoader, "build", object : XC_MethodHook() {
            override fun beforeHookedMethod(param: MethodHookParam?) {
                xLog("应用程序相机请求创建CaptureRequest.Build         屏幕surface: $virtualSurface       ijkm: $ijkMediaPlayer")
                val builder = (param?.thisObject as? CaptureRequest.Builder)
                // 8.6.1 获取Surface集合
                val request = XposedHelpers.getObjectField(builder, "mRequest") as CaptureRequest
                val surfaceSet = XposedHelpers.getObjectField(request, "mSurfaceSet") as MutableSet<*>
                xLog("surfaceSet: $surfaceSet")
            }
        })

        // 8.7 监控TextureView创建
        val clazz = XposedHelpers.findClass("android.view.TextureView", lpparam.classLoader)
        xLog("TextureView: $clazz")
        XposedBridge.hookAllConstructors(clazz, object : XC_MethodHook() {
            override fun afterHookedMethod(param: MethodHookParam?) {
                xLog("TextureView: ${param?.thisObject}")
            }
        })
    }

    /**
     * 9. Hook会话状态回调
     * 用于调试相机管道创建状态
     */
    private fun hookSessionStateCallback(clazz: Class<*>) {
        if (sessionStateCallbackClazz != null) {
            xLog("已经hook过，不再hook")
            return
        }
        sessionStateCallbackClazz = clazz

        // 9.1 监控配置成功回调
        XposedHelpers.findAndHookMethod(sessionStateCallbackClazz, "onConfigured", 
            CameraCaptureSession::class.java, object : XC_MethodHook() {
            override fun beforeHookedMethod(param: MethodHookParam) {
                xLog("相机管道创建完毕onConfigured             ")
            }
        })

        // 9.2 监控配置失败回调
        XposedHelpers.findAndHookMethod(sessionStateCallbackClazz, "onConfigureFailed", 
            CameraCaptureSession::class.java, object : XC_MethodHook() {
            override fun beforeHookedMethod(param: MethodHookParam) {
                xLog("相机管道创建失败onConfigureFailed             ")
            }
        })
    }
}